/*
 * graph2D
 * Copyright (c) 2009 Shun Moriya <shun@mnu.sakura.ne.jp>
 *
 * The MIT License
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "message.h"
#include "text.h"

namespace Graph2D
{
	Message::Message()
		: delayTimer(0)
		, length(0)
		, timer(1.0f / 18.0f)
	{
		setType(TYPE_MESSAGE);

		setColor(0, 0, 0, 0, 0);
		setFrameStyle(FRAME_STYLE_NOLINE|FRAME_STYLE_FLAT);

		text = new Text();
		text->setColor(0, 0, 0, 0, 0.0f);
		text->setPosition(0, 0);
		set(0, text);

		compact();
	}

	Message::~Message()
	{
		clear();

		if(text)
		{
//			text->release();
		}
	}

	void Message::clear()
	{
		text->clear();

		message.clear();

		length = 0;
	}

	const std::string& Message::getString() const
	{
		return message;
	}

	void Message::setString(const std::string& message)
	{
		clear();

		this->message = message;
		this->wait = 0;
		this->delayWait = 0;
		this->clicked = false;
	}

	void Message::format(const char* format, ...)
	{
		va_list ap;
		va_start(ap, format);
		text->vformat(format, ap);
		va_end(ap);
	}

	void Message::vformat(const char* format, va_list ap)
	{
		text->vformat(format, ap);
	}

	void Message::replace(const char* key, const char* word)
	{
		text->replace(key, word);
	}

	const Color& Message::getTextColor() const
	{
		return text->getTextColor();
	}

	void Message::setTextColor(const Color& color)
	{
		text->setTextColor(color);
	}

	void Message::setTextColor(const float r, const float g, const float b, const float a)
	{
		setTextColor(Color(r, g, b, a));
	}

	Text& Message::getText()
	{
		return *text;
	}

	const Text& Message::getText() const
	{
		return *text;
	}

	float Message::getTimer() const
	{
		return timer;
	}

	void Message::setTimer(const float timer)
	{
		this->timer = timer;
	}

	float Message::getDelayTimer() const
	{
		return delayTimer;
	}

	/*!
	 * ディレイタイマーを設定します
	 * @param[in]	delayTimer	0ならタッチを待ちます。正の値なら指定秒数後閉じます。負の値ならば表示したままです。
	 */
	void Message::setDelayTimer(const float delayTimer)
	{
		this->delayTimer = delayTimer;
	}

	bool Message::finished() const
	{
		return clicked && length >= text->getString().length();
	}

	void Message::onSerialize(mana_stream* stream) const
	{
		super::onSerialize(stream);

		mana_stream_mark(stream);
		mana_stream_push_string(stream, const_cast<char*>(message.c_str()));
		mana_stream_push_unsigned_integer(stream, text->getID());
		mana_stream_push_float(stream, wait);
		mana_stream_push_float(stream, timer);
		mana_stream_push_float(stream, delayWait);
		mana_stream_push_float(stream, delayTimer);
		mana_stream_push_unsigned_integer(stream, length);
		mana_stream_push_unsigned_char(stream, clicked);
		mana_stream_mark(stream);
	}

	void Message::onDeserialize(mana_stream* stream)
	{
		super::onDeserialize(stream);

		mana_stream_check(stream);
		message = mana_stream_get_string_pointer(stream);
		mana_steram_seek(stream, mana_stream_get_string_length(stream) + 1);
		text = static_cast<Text*>(Component::find(mana_stream_pop_unsigned_integer(stream)));
		wait = mana_stream_pop_float(stream);
		timer = mana_stream_pop_float(stream);
		delayWait = mana_stream_pop_float(stream);
		delayTimer = mana_stream_pop_float(stream);
		length = mana_stream_pop_unsigned_integer(stream);
		clicked = mana_stream_pop_unsigned_char(stream) ? true : false;
		mana_stream_check(stream);
	}

	void Message::onUpdate(const UpdateInfomation& updateInfomation)
	{
		if(!message.empty())
		{
			if(length < message.length())
			{
				wait += updateInfomation.deltaTime;
				while(wait >= timer)
				{
					// UTF-8のマルチバイト分スキップ
					unsigned int count = 0;
					unsigned char flag = 0x80;
					const unsigned char code = message.at(length);
					while(flag & code)
					{
						flag >>= 1;
						count++;
					}
					if(count <= 0)
						count = 1;
					length += count;

					// メッセージを全て表示した？
					if(length >= message.length())
					{
						length = message.length();
						break;
					}

					wait -= timer;
				}
			}
			else
			{
				if(delayTimer > 0)
				{
					delayWait += updateInfomation.deltaTime;
					if(delayWait >= delayTimer)
						clicked = true;
				}
			}

			if(text->getString().length() != length)
			{
				text->setString(message.substr(0, length));
			}
		}

		super::onUpdate(updateInfomation);
	}

#if defined(DEBUG) || defined(_DEBUG) || defined(_DEBUG_) || !defined(NDEBUG)
	bool Message::onDraw(const DrawRect& drawRect)
	{
		return super::onDraw(drawRect);
	}
#endif

	bool Message::onTouchesEnded(const Vector2& localPosition)
	{
		if(delayTimer == 0)
		{
			if(length >= message.length())
			{
				clicked = true;
			}
			else
			{
				length = message.length();
			}
		}

		return super::onTouchesEnded(localPosition);
	}

	bool Message::compare(const Message& other) const
	{
		if(!super::compare(other))
			return false;

		if(message != other.message)
			return false;
		if(text && text->compare(*other.text) == false)
			return false;
		if(wait != other.wait)
			return false;
		if(timer != other.timer)
			return false;
		if(delayWait != other.delayWait)
			return false;
		if(delayTimer != other.delayTimer)
			return false;
		if(length != other.length)
			return false;
		if(clicked != other.clicked)
			return false;

		return true;
	}
}
